<?php
namespace FastPHP\Core;

class ExceptionHandler
{
    protected static $debug = true;
    protected static $logFile = '';

    public static function register()
    {
        self::$debug = !empty(config('app.debug'));
        self::$logFile = APP_PATH . 'runtime/error.log';
        set_exception_handler([self::class, 'handle']);
        set_error_handler([self::class, 'handleError']);
        register_shutdown_function([self::class, 'handleShutdown']);
    }

    public static function handle($e)
    {
        if ($e instanceof BaseException) {
            $code = $e->getCode() ?: 500;
            $msg = $e->getMessage();
            $data = $e->getData();
            self::logError($e);
            self::render($msg, $code, $e, $data);
        } elseif ($e instanceof HttpException) {
            $code = $e->getHttpCode();
            $msg = $e->getMessage();
            self::logError($e);
            self::render($msg, $code, $e);
        } elseif ($e instanceof ValidateException) {
            $code = $e->getCode() ?: 422;
            $msg = $e->getMessage();
            $data = $e->getData();
            self::logError($e);
            self::render($msg, $code, $e, $data);
        } elseif ($e instanceof \Throwable) {
            $code = $e->getCode() ?: 500;
            $msg = $e->getMessage();
            self::logError($e);
            self::render($msg, $code, $e);
        }
    }

    public static function handleError($errno, $errstr, $errfile, $errline)
    {
        $msg = "[$errno] $errstr in $errfile on line $errline";
        self::logError($msg);
        throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
    }

    public static function handleShutdown()
    {
        $error = error_get_last();
        if ($error && in_array($error['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR])) {
            $msg = "[{$error['type']}] {$error['message']} in {$error['file']} on line {$error['line']}";
            self::logError($msg);
            self::render($msg, 500);
        }
    }

    protected static function render($message, $code = 500, $exception = null, $data = null)
    {
        http_response_code($code);
        $isJson = self::isJsonRequest();
        $debug = self::$debug;
        $trace = $exception ? $exception->getTraceAsString() : '';
        
        // 获取自定义错误信息配置
        $errorMessages = config('app.error_messages', []);
        
        if ($isJson) {
            $result = [
                'code' => $code,
                'msg' => $debug ? $message : ($errorMessages[$code] ?? '页面出错了，无法访问'),
                'data' => $data,
            ];
            if ($debug && $trace) {
                $result['trace'] = $trace;
            }
            header('Content-Type: application/json; charset=utf-8');
            echo json_encode($result, JSON_UNESCAPED_UNICODE);
        } else {
            $msg = $debug ? $message : ($errorMessages[$code] ?? '页面出错了，无法访问');
            include ROOT_PATH . 'FastPHP/tpl/error.php';
        }
        exit;
    }

    protected static function isJsonRequest()
    {
        if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') {
            return true;
        }
        if (!empty($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'application/json') !== false) {
            return true;
        }
        if (isset($_SERVER['CONTENT_TYPE']) && strpos($_SERVER['CONTENT_TYPE'], 'application/json') !== false) {
            return true;
        }
        return false;
    }

    protected static function logError($error)
    {
        if (!is_dir(APP_PATH . 'runtime')) {
            mkdir(APP_PATH . 'runtime', 0777, true);
        }
        $date = date('Y-m-d H:i:s');
        if ($error instanceof \Throwable) {
            $msg = "[$date] " . get_class($error) . ': ' . $error->getMessage() . " in " . $error->getFile() . " on line " . $error->getLine() . "\n" . $error->getTraceAsString() . "\n";
        } else {
            $msg = "[$date] $error\n";
        }
        file_put_contents(self::$logFile, $msg, FILE_APPEND);
    }
} 